#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>

// 日志等级
enum Level
{
    DEBUG = 0, // 调试信息
    INFO,      // 常规信息
    WARN,      // 警告
    ERROR,     // 错误
    FATAL      // 严重错误
};

// 打印方式--往哪里打印
enum Way
{
    Screen = 10, // 屏幕
    OneFile,     // 文件
    ClassFile    // 多个文件
};

// 默认打印方式
const Way default_way = Screen;
// 默认生成日志的目录
const std::string logdir = "log";
// 默认日志文件名后缀
const std::string default_filename = ".log";

class Log
{
private:
    Way _way;
    std::string _filename;

private:
    // 将等级转化为字符串
    std::string LevelToString(const Level level)
    {
        switch (level)
        {
        case DEBUG:
            return "Debug";
        case INFO:
            return "Info";
        case WARN:
            return "Warning";
        case ERROR:
            return "Error";
        case FATAL:
            return "Fatal";
        default:
            return "Undefined level";
        }
    }
    // 时间戳转化字符串
    std::string TimeStampToString()
    {
        // 获取时间--返回当前时间戳
        time_t current_time = time(nullptr);
        // 转化为结构化的数据
        struct tm *current = localtime(&current_time);

        // 结构化数据组成字符串
        char time_buffer[128];
        snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
                 current->tm_year + 1900, current->tm_mon + 1, current->tm_mday,
                 current->tm_hour, current->tm_min, current->tm_sec);
        return std::string(time_buffer);
    }
    // 向一个文件写入
    void WriteLogToOneFile(const std::string &log_path, const std::string &message)
    {
        // 设置权限掩码
        umask(0);
        // 打开文件，不存在就创建
        int fd = open(log_path.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
        // 写入文件
        write(fd, message.c_str(), message.size());
        // 关闭文件
        close(fd);
    }
    // 生成路径文件名
    void WriteLogToClassFile(const std::string &levelstr, const std::string &message)
    {
        // 拼接路径
        std::string log_path = "./";
        log_path += logdir;
        log_path += '/';
        log_path += levelstr;
        log_path += _filename;
        // 调用写入文件的接口
        WriteLogToOneFile(log_path, message);
    }
    // 决定如何写入文件
    void WirteLog(const std::string &levelstr, const std::string &message)
    {
        switch (_way)
        {
        case Screen:
            std::cout << message << std::endl;
            break;
        case OneFile:
            WriteLogToClassFile("all", message);
            break;
        case ClassFile:
            WriteLogToClassFile(levelstr, message);
            break;
        default:
            break;
        }
    }

public:
    // 构造函数
    Log(const Way way = default_way, const std::string filename = default_filename)
        : _way(way), _filename(filename)
    {
        // 如果是向文件中打印，那么创建目录
        if (_way != default_way)
        {
            mkdir(logdir.c_str(), 0775);
        }
    }
    // 向外提供的用户写日志的接口
    void LOG(Level level, const char *format, ...)
    {
        char left_buffer[1024];
        // 获取日志等级、时间以及线程id字符串
        std::string levelstr = LevelToString(level);
        std::string current_time = TimeStampToString();
        std::string thread_id = std::to_string(getpid());
        // 拼成格式化的字符串
        snprintf(left_buffer, sizeof(left_buffer), "[%s][%s][%s]",
                 levelstr.c_str(), current_time.c_str(), thread_id.c_str());

        char right_buffer[1024];
        // 将用户打印的内容解析出来转化为字符串
        va_list args;
        va_start(args, format);
        vsnprintf(right_buffer, sizeof(right_buffer), format, args);
        va_end(args);

        // 拼成一个大的输出日志信息
        std::string log_message = left_buffer;
        log_message += right_buffer;
        // 调用打印函数
        WirteLog(levelstr, log_message);
    }
};